package main

import (
	"fmt"
	"math/rand"
	"sort"
	"strings"

	"main/role"
	"main/util"
)

type Stage func()

type PlayerVote struct {
	PlayerNum  int
	NumOfVotes float64
}

type PlayersVote []*PlayerVote

type Stages struct {
	day             int
	idx             int
	stages          []Stage
	game            *Game
	voteTotal       PlayersVote //投票组
	noVoteTotal     []int       //弃票组
	lastNight       []func()
	policeGroup     Players
	givePoliceGroup Players //退水组
	nopoliceGroup   Players //警下组
	seerKnow        Players
}

func NewStages(g *Game) *Stages {
	s := &Stages{
		day:      0,
		idx:      0,
		game:     g,
		seerKnow: Players{g.players.GetSeer()},
	}
	s.stages = []Stage{s.FirstNightStage, s.FirstDayStage, s.CommonStage}
	return s
}

func (s *Stages) Play() {
	for s.idx < len(s.stages) {
		s.stages[s.idx]()
		s.idx++
	}
}

func (s *Stages) delay(f func()) {
	s.lastNight = append(s.lastNight, f)
}

func (s *Stages) doLastNight() {
	for _, f := range s.lastNight {
		f()
	}
	s.lastNight = s.lastNight[0:0]
}

func (s *Stages) IsOver() bool {
	isOver, who := s.game.IsOver()
	if isOver {
		s.idx = len(s.stages) // over
		if who == role.BadMan {
			util.Printf("狼人胜利！\n")
		} else {
			util.Printf("好人胜利！\n")
		}
	}
	return isOver
}

// FirstNightStage 首夜阶段
func (s *Stages) FirstNightStage() {
	util.Printf("当前阶段：首夜\n")
	util.Printf("天黑请闭眼\n")
	killed := s.werewolfStage()
	s.witch(true, killed)
	s.seerStage()
	s.day++
}

// FirstDayStage 第一天
func (s *Stages) FirstDayStage() {
	util.Printf("当前阶段：第%d天\n", s.day)
	util.Printf("天亮了\n")
	// TODO 警长竞选阶段
	s.PoliceStage()
	s.doLastNight()
	if s.IsOver() {
		return
	}
	s.dayStage()
	if s.IsOver() {
		return
	}
	util.Printf("天黑请闭眼\n")
	killed := s.werewolfStage()
	s.witch(false, killed)
	s.seerStage()
	s.day++
}

// CommonStage .
func (s *Stages) CommonStage() {
	for {
		util.Printf("当前阶段：第%d天\n", s.day)
		util.Printf("天亮了\n")
		s.doLastNight()
		if s.IsOver() {
			return
		}
		s.dayStage()
		if s.IsOver() {
			return
		}
		util.Printf("天黑请闭眼\n")
		killed := s.werewolfStage()
		s.witch(false, killed)
		s.seerStage()
		s.day++
	}
}

func (s *Stages) dayStage() {
	// 白天阶段 TODO 投票流程
	s.VoteStage()
}

func (s *Stages) werewolfStage() *Player {
	util.Printf("狼人请睁眼\n")
	util.Printf("狼人请杀人\n")
	return s.randomKill()
}

func (s *Stages) witch(canReviveSelf bool, killed *Player) {
	util.Printf("女巫请睁眼\n")
	if killed != nil {
		util.Printf("昨晚%d号玩家死了，你有一瓶解药，要救吗？\n", killed.ID)
	}
	util.Printf("毒谁？\n")

	players := s.game.players
	witch := players.GetWitch()

	if killed != nil && killed.ID == witch.ID && !canReviveSelf {
		// 不能自救时，直接进入毒阶段
		s.witchPoisonStage()
		return
	}

	doSave := util.HighProbabilityDo()
	if killed != nil && witch.Role.IsAlive() && witch.Witch().CanRevive() && doSave {
		s.delay(func() {
			witch.Witch().Revive(killed.Role.GetBase())
		})
		util.Printf("女巫使用解药，%d号玩家被救\n", killed.ID)
		return
	}

	s.witchPoisonStage()
}

func (s *Stages) witchPoisonStage() {
	players := s.game.players
	witch := players.GetWitch()
	doPoison := util.HalfProbabilityDo()

	if witch.Role.IsAlive() && witch.Witch().CanPoison() && doPoison {
		alive := players.GetAlive()
		poisoned := alive[rand.Intn(len(alive))]
		s.delay(func() {
			witch.Witch().Poison(poisoned.Role.GetBase())
		})
		util.Printf("女巫使用毒药，%d被毒死\n", poisoned.ID)
	}
}

func (s *Stages) seerStage() {
	util.Printf("预言家请睁眼\n")
	util.Printf("预言家请选择一位玩家查验身份\n")

	players := s.game.players
	seer := players.GetSeer()
	if seer.Role.IsAlive() {
		alive := players.GetAlive()
		seerKnowMap := make(map[*Player]struct{})
		for _, player := range s.seerKnow {
			seerKnowMap[player] = struct{}{}
		}
		canSee := make(Players, 0)
		for _, player := range alive {
			if _, ok := seerKnowMap[player]; !ok {
				canSee = append(canSee, player)
			}
		}
		if len(canSee) == 0 {
			util.Printf("身份全部明朗，无需查验\n")
			return
		}
		player := canSee[rand.Intn(len(canSee))]
		identity := seer.Seer().See(player.Role.GetBase())
		util.Printf("%d号玩家的身份是 ", player.ID)
		util.Printf("%s\n", identity)
	}
}

type killVote struct {
	killed     *Player
	werewolves Players
}

func (s *Stages) randomKill() *Player {
	players := s.game.players
	werewolves := players.GetWerewolves().GetAlive()
	alive := players.GetAlive()

	voteWerewolves := make([]killVote, 0, len(alive))
	for _, player := range alive {
		voteWerewolves = append(voteWerewolves, killVote{
			killed:     player,
			werewolves: nil,
		})
	}

	notVoteWerewolves := make(Players, 0)
	for _, werewolf := range werewolves {
		if util.HighProbabilityDo() {
			r := rand.Intn(len(alive))
			voteWerewolves[r].werewolves = append(voteWerewolves[r].werewolves, werewolf)
		} else {
			notVoteWerewolves = append(notVoteWerewolves, werewolf)
		}
	}

	sort.Slice(voteWerewolves, func(i, j int) bool {
		if len(voteWerewolves[i].werewolves) == len(voteWerewolves[j].werewolves) {
			return voteWerewolves[i].killed.ID > voteWerewolves[j].killed.ID
		}
		return len(voteWerewolves[i].werewolves) > len(voteWerewolves[j].werewolves)
	})

	for _, voteInfo := range voteWerewolves {
		if len(voteInfo.werewolves) == 0 {
			continue
		}
		util.Printf("%s玩家选择击杀 %d号玩家\n", voteInfo.werewolves.GetIDString(), voteInfo.killed.ID)
	}
	if len(notVoteWerewolves) > 0 {
		util.Printf("%s玩家没有选择击杀目标\n", notVoteWerewolves.GetIDString())
	}

	maxVote := len(voteWerewolves[0].werewolves)

	if maxVote == 0 {
		return nil
	}

	sameVote := make([]killVote, 0)
	for _, voteInfo := range voteWerewolves {
		if len(voteInfo.werewolves) == maxVote {
			sameVote = append(sameVote, voteInfo)
		}
	}

	var res *Player
	if len(sameVote) == 1 {
		res = sameVote[0].killed
	} else {
		util.Printf("产生平票，即将随机选择一名玩家\n")
		res = sameVote[rand.Intn(len(sameVote))].killed
	}

	s.delay(func() {
		werewolves[0].Werewolf().Kill(res.Role.GetBase())
	})
	util.Printf("%d号玩家被击杀\n", res.ID)

	return res
}

func (s *Stages) PoliceStage() {
	//上警
	for _, player := range s.game.players {
		if util.HalfProbabilityDo() {
			s.policeGroup = append(s.policeGroup, player)
			util.Printf("玩家%d上警\n", player.ID)
			util.Printf("警上名单%v\n", s.policeGroup.GetIDString())
		} else {
			s.nopoliceGroup = append(s.nopoliceGroup, player)
		}
	}
	if len(s.policeGroup) == 0 {
		util.Printf("无人上警,警徽流失\n")
		return
	}
	util.Printf("----上警阶段结束----\n")
	//随机退水阶段
	for i := len(s.policeGroup) - 1; i >= 0; i-- {
		if util.HalfProbabilityDo() && len(s.policeGroup) > 1 {
			util.Printf("玩家%d退水\n", s.policeGroup[i].ID)
			s.givePoliceGroup = append(s.givePoliceGroup, s.policeGroup[i])
			s.policeGroup = DelByIndex(s.policeGroup, SearchIndex(s.policeGroup, s.policeGroup[i].ID))
			if len(s.policeGroup) == 1 {
				s.policeGroup[0].Role.GetBase().ChangeVoteVaule()
				util.Printf("%d玩家当选警长\n", s.policeGroup[0].ID)
				return
			}
			util.Printf("警上名单%v\n", s.policeGroup.GetIDString())
			util.Printf("退水名单%v\n", s.givePoliceGroup.GetIDString())

		} else if len(s.policeGroup) == 1 {
			s.policeGroup[0].Role.GetBase().ChangeVoteVaule()
			util.Printf("%d玩家当选警长\n", s.policeGroup[0].ID)
			return
		} else {
			s.voteTotal = append(s.voteTotal, &PlayerVote{
				PlayerNum:  s.policeGroup[i].ID,
				NumOfVotes: 0,
			})
		}
	}
	util.Printf("----退水阶段结束----\n")
	//投票阶段 退水玩家不可投票
	for _, player := range s.nopoliceGroup {
		if util.HighProbabilityDo() {
			atariNum := util.RandNum(len(s.voteTotal))
			if len(s.nopoliceGroup) == 1 { //可以弃票 投谁即当选
				s.policeGroup[SearchIndex(s.policeGroup, s.voteTotal[atariNum].PlayerNum)].Role.GetBase().ChangeVoteVaule()
				util.Printf("%d投%d\n", player.ID, s.voteTotal[atariNum].PlayerNum)
				util.Printf("%d玩家当选警长\n", s.voteTotal[atariNum].PlayerNum)
				s.voteTotal = PlayersVote{}
				return
			} else {
				s.voteTotal[atariNum].NumOfVotes++
				util.Printf("%d投%d\n", player.ID, s.voteTotal[atariNum].PlayerNum)
			}
		} else {
			util.Printf("%d弃票\n", player.ID)
			if len(s.nopoliceGroup) == 1 {
				util.Printf("警徽流失\n")
				return
			}
		}
	}
	util.Printf("----投票阶段结束----\n")
	util.Printf("投票情况%v\n", s.voteTotal.GetVoteString())
	if s.CountVote() {
		s.game.players[s.voteTotal[0].PlayerNum-1].Role.GetBase().ChangeVoteVaule()
		util.Printf("%d玩家当选警长\n", s.voteTotal[0].PlayerNum)
		s.voteTotal = PlayersVote{}
		return
	} else {
		s.ReleaseVoteTotal()
	}
	//fmt.Println(s.voteTotal, "投票情况")
	//平票阶段 退水玩家也可投票
	s.nopoliceGroup = append(s.nopoliceGroup, s.givePoliceGroup...)
	for _, player := range s.nopoliceGroup {
		equalTurnNum := util.RandNum(len(s.voteTotal))
		s.voteTotal[equalTurnNum].NumOfVotes++
		util.Printf("%d投%d\n", player.ID, s.voteTotal[equalTurnNum].PlayerNum)
	}
	util.Printf("投票情况%v\n", s.voteTotal.GetVoteString())
	if !s.CountVote() {
		util.Printf("2次平票,警徽流失\n")
		s.voteTotal = PlayersVote{}
	} else {
		util.Printf("%d玩家当选警长\n", s.voteTotal[0].PlayerNum)
	}
}

func (s *Stages) ReleaseVoteTotal() {
	for _, v := range s.voteTotal {
		v.NumOfVotes = 0
	}
}

func (s *Stages) CountVote() bool { //bool 是否决出
	players := PlayersVote{}
	sort.Slice(s.voteTotal, func(i, j int) bool {
		return s.voteTotal[i].NumOfVotes > s.voteTotal[j].NumOfVotes
	})
	l, r := 0, len(s.voteTotal)-1
	for l < r {
		if s.voteTotal[l].NumOfVotes == s.voteTotal[r].NumOfVotes && r-l+1 > 1 {
			for i := l; i <= r; i++ {
				players = append(players, s.voteTotal[i])
			}
			util.Printf("%d人平票PK\n", r-l+1)
			util.Printf("平票pk名单%v\n", players.GetIDString())
			s.voteTotal = players
			return false
		}
		r--
	}
	util.Printf("%d得票最多\n", s.voteTotal[0].PlayerNum)
	return true
}

func SearchIndex(object []*Player, id int) int {
	return sort.Search(len(object), func(i int) bool {
		return object[i].ID >= id
	})
}

func DelByIndex(object []*Player, index int) []*Player {
	if index == 0 {
		object = object[index+1:]
	} else if index == len(object)-1 {
		object = object[:index]
	} else {
		object = append(object[:index], object[index+1:]...)
	}
	return object
}

func (p PlayersVote) GetVoteString() string {
	builder := strings.Builder{}
	for i, player := range p {
		builder.WriteString(fmt.Sprintf("%d号得票%v", player.PlayerNum, player.NumOfVotes))
		if i != len(p)-1 {
			builder.WriteString("，")
		}
	}
	return builder.String()
}

func (p PlayersVote) GetIDString() string {
	builder := strings.Builder{}
	for i, player := range p {
		builder.WriteString(fmt.Sprintf("%d号", player.PlayerNum))
		if i != len(p)-1 {
			builder.WriteString("，")
		}
	}
	return builder.String()
}

func (s *Stages) VoteStage() {
	players := s.game.players.GetAlive()
	for _, player := range players {
		s.voteTotal = append(s.voteTotal, &PlayerVote{
			PlayerNum:  player.ID,
			NumOfVotes: 0,
		})
	}
	util.Printf("----开始投票阶段----\n")
	for _, player := range players {
		randNum := util.RandNum(len(s.voteTotal))
		if util.HighProbabilityDo() {
			s.voteTotal[randNum].NumOfVotes += player.Role.GetBase().GetVoteVaule()
			util.Printf("玩家%d号投玩家%d号\n", player.ID, s.voteTotal[randNum].PlayerNum)
		} else {
			util.Printf("玩家%d号弃票\n", player.ID)
		}
	}
	fmt.Println("投票情况", s.voteTotal.GetVoteString())
	if s.CountVote() {
		index := SearchIndex(players, s.voteTotal[0].PlayerNum)
		players[index].Role.VoteOut()
		util.Printf("%d号玩家公投出局\n", s.voteTotal[0].PlayerNum)
		s.voteTotal = PlayersVote{}
		return
	} else {
		s.ReleaseVoteTotal()
	}
	//平票阶段
	//去除平票玩家的投票权

	for i := len(players) - 1; i >= 0; i-- {
		for _, v := range s.voteTotal {
			if players[i].ID == v.PlayerNum {
				players = DelByIndex(players, i)
				break
			}
		}
	}

	for _, player := range players {
		randNum := util.RandNum(len(s.voteTotal))
		if util.HighProbabilityDo() {
			s.voteTotal[randNum].NumOfVotes += player.Role.GetBase().GetVoteVaule()
			util.Printf("玩家%d号投玩家%d号\n", player.ID, s.voteTotal[randNum].PlayerNum)
		} else {
			util.Printf("玩家%d号弃票\n", player.ID)
		}
	}

	fmt.Println("投票情况", s.voteTotal.GetVoteString())
	if s.CountVote() {
		players = s.game.players.GetAlive()
		index := SearchIndex(players, s.voteTotal[0].PlayerNum)
		players[index].Role.VoteOut()
		util.Printf("%d号玩家公投出局\n", s.voteTotal[0].PlayerNum)
		s.voteTotal = PlayersVote{}
	} else {
		s.voteTotal = PlayersVote{}
		util.Printf("再次平票 无人出局\n")
	}
}
